package com.citybox.shared.utils.minio;


import com.citybox.shared.config.MinioConfig;
import com.citybox.shared.web.domain.R;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Description Minio工具类
 */
@Slf4j
@Component
public class MinioUtils {

    @Resource
    private MinioClient minioClient;

    @Resource
    private MinioConfig configuration;

    /**
     * @param name 名字
     * @Description description: 判断bucket是否存在，不存在则创建
     */
    public boolean existBucket(String name) {
        boolean exists;
        try {
            exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());
                exists = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            exists = false;
        }
        return exists;
    }

    /**
     * @param bucketName 存储bucket名称
     * @Description 创建存储bucket
     */
    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * @param bucketName 存储bucket名称
     * @Description 删除存储bucket
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * @param fileName 文件名称
     * @param time     时间
     * @Description 获取上传临时签名
     */
    @SneakyThrows
    public Map getPolicy(String fileName, ZonedDateTime time) {
        PostPolicy postPolicy = new PostPolicy(configuration.getBucketName(), time);
        postPolicy.addEqualsCondition("key", fileName);
        try {
            Map<String, String> map = minioClient.getPresignedPostFormData(postPolicy);
            HashMap<String, String> map1 = new HashMap<>();
            map.forEach((k, v) -> {
                map1.put(k.replaceAll("-", ""), v);
            });
            map1.put("host", configuration.getUrl() + "/" + configuration.getBucketName());
            return map1;
        } catch (ErrorResponseException e) {
            e.printStackTrace();
        } catch (InsufficientDataException e) {
            e.printStackTrace();
        } catch (InternalException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidResponseException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (ServerException e) {
            e.printStackTrace();
        } catch (XmlParserException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @param objectName 对象名称
     * @param method     方法
     * @param time       时间
     * @param timeUnit   时间单位
     * @Description 获取上传文件的url
     */
    public String getPolicyUrl(String objectName, Method method, int time, TimeUnit timeUnit) {
        try {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(method)
                    .bucket(configuration.getBucketName())
                    .object(objectName)
                    .expiry(time, timeUnit).build());
        } catch (ErrorResponseException e) {
            e.printStackTrace();
        } catch (InsufficientDataException e) {
            e.printStackTrace();
        } catch (InternalException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidResponseException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (XmlParserException e) {
            e.printStackTrace();
        } catch (ServerException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * @param file 文件
     * @param fileTypeConfig 可上传文件类型
     * @param fileSuffixConfig 可上传文件后缀
     * @return 文件url
     */
    public Map<String,Object> upload(MultipartFile file,String fileTypeConfig, String fileSuffixConfig) {
        log.info("开始上传----------------------");

        Map<String,Object> resultMap = new HashMap<>();
        try {
            //判断文件大小(不能超过30MB)
            long maxFileSize = 30 * 1024 * 1024;
            if(file.getSize() > maxFileSize){
                resultMap.put("success","error");
                resultMap.put("msg","文件大小不能超过30MB");
                return resultMap;
            }

            //判断文件后缀是否可上传文件&&判断文件大小是否可上传
            if(!isUpload(file,fileTypeConfig,fileSuffixConfig)){
                resultMap.put("success","error");
                resultMap.put("msg","禁止上传该类型文件");
                return resultMap;
            }

            //重命名文件名
            String fileName = System.currentTimeMillis() + "_" + file.getOriginalFilename().replaceAll("[^a-zA-Z0-9_\\-\\.]", "_");
            // 文件名后缀(不带点)
            String fileSuffix = getFileSuffix(fileName);
            // 文件名前缀(不带点)
            String name = fileName.substring(0,fileName.lastIndexOf("."));

            try(InputStream inputStream = file.getInputStream()) {
                minioClient.putObject(PutObjectArgs.builder()
                        .bucket(configuration.getBucketName())
                        .object(fileName)
                        .stream(inputStream, file.getSize(), -1)
                        .contentType(file.getContentType())
                        .build());
            }

            //上传后的文件url
            String fileUrl = configuration.getUrl() + "/" + configuration.getBucketName() + "/" + fileName;

            resultMap.put("success","success");
            resultMap.put("fileSuffix",fileSuffix);
            resultMap.put("fileName",name);
            resultMap.put("fileType",file.getContentType());
            resultMap.put("fileUrl",fileUrl);
        } catch (Exception e) {
            log.error("文件上传异常--------------------error");
            e.printStackTrace();
            resultMap.put("error","minio上传异常" + e.toString());
        }
        log.info("文件上传完成-----------------end");
        return resultMap;
    }

    /**
     * @Description description: 下载文件
     */
    /*public byte[] download(String fileName) {
        InputStream in = null;
        ByteArrayOutputStream out = null;
        byte[] bytes = null;
        try {
            in = minioClient.getObject(GetObjectArgs.builder().bucket(configuration.getBucketName()).object(fileName).build());
            out = new ByteArrayOutputStream();
            IOUtils.copy(in, out);
            //封装返回值
            bytes = out.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return bytes;
    }*/

    /**
     * 删除文件
     * @param fileNameList 文件名称集合
     */
    public R<?> removeFile(List<String> fileNameList){
        try {
            for(String fileName : fileNameList){
                //删除服务器文件
                minioClient.removeObject(RemoveObjectArgs.builder().bucket(configuration.getBucketName()).object(fileName).build());
            }
            return R.success();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("MinioUtil-uploadMinio-error", e);
        }
        return R.fail();
    }


    /**
     * 判断文件类型and文件后缀是否可以上传
     * @param file
     * @param fileTypeConfig 可上传的文件类型
     * @param fileSuffixConfig 可上传的文件后缀
     * @return
     */
    public boolean isUpload(MultipartFile file,String fileTypeConfig, String fileSuffixConfig){
        // 检查文件是否为空
        if (file.isEmpty()) {
            return false;
        }

        //文件后缀
        String fileSuffix = getFileSuffix(file.getOriginalFilename());
        //文件类型
        String fileType = file.getContentType();

        //配置的可上传文件类型
        List<String> fileTypeList = Arrays.stream(fileTypeConfig.split("#"))
                .collect(Collectors.toList());
        boolean typeState = fileTypeList.stream().anyMatch(type ->type.equalsIgnoreCase(fileType));
        if (!typeState) {
            return false;
        }

        //查询配置的可上传文件后缀
        List<String> fileSuffixList = Arrays.stream(fileSuffixConfig.split("#"))
                .collect(Collectors.toList());
        return fileSuffixList.stream().anyMatch(suffix ->suffix.equalsIgnoreCase(fileSuffix));
    }

    /**
     * 获取文件后缀
     * @param fileName 文件名称带后缀or文件地址
     * @return
     */
    public String getFileSuffix(String fileName) {
        // 通过lastIndexOf()函数找到最后一个"."的位置
        int lastDotPos = fileName.lastIndexOf(".");
        String suffix = "";
        if (lastDotPos != -1 && lastDotPos < fileName.length()) {
            // 从最后一个"."开始提取子字符串作为后缀名
            suffix = fileName.substring(lastDotPos + 1);
        }
        return suffix;
    }

}
